/* eslint-disable @typescript-eslint/no-unsafe-member-access */
// 在 TypeScript 中，方法装饰器（Method Decorators）用于修饰类的方法。它们可以用于修改方法的行为、添加元数据、进行日志记录、权限检查等。方法装饰器的目标是类的方法，其签名为

// (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void | PropertyDescriptor。
// 其中，target 是方法所在的类的原型对象，propertyKey 是方法的名称，descriptor 是方法的描述符。方法装饰器可以返回一个新的描述符，也可以直接修改原始描述符。
/**
 * 2.2.3.1 方法装饰器的语法 #
方法装饰器是一个接受三个参数的函数：

target：装饰的目标对象，对于静态成员来说是类的构造函数，对于实例成员是类的原型对象。
propertyKey：装饰的成员名称。
descriptor：成员的属性描述符。
 */
// 2.2.3.2 日志记录
// 可以在方法调用前后记录日志，跟踪方法调用情况。

/**
 * 方法装饰器，用于在方法调用前后记录日志
 * @param target - 装饰的目标对象，对于静态成员来说是类的构造函数，对于实例成员是类的原型对象
 * @param propertyKey - 装饰的成员名称
 * @param descriptor - 成员的属性描述符
 * @returns 修改后的属性描述符
 */
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  // 保存原始方法
  const originalMethod = descriptor.value;
  // 修改属性描述符的 value 属性，用新的函数替换原始方法
  descriptor.value = function (...args: any[]) {
    // 记录方法调用信息，包含方法名和传入的参数
    console.log(`Calling ${propertyKey} with arguments: ${args}`);
    // 调用原始方法并传入参数，获取返回结果
    const result = originalMethod.apply(this, args);
    // 记录方法调用的返回结果
    console.log(`Result: ${result}`);
    // 返回原始方法的执行结果
    return result;
  };
  // 返回修改后的属性描述符
  return descriptor;
}

//例子
const users = {
  '001': { roles: ['admin'] },
  '002': { roles: ['user'] },
};
// 方法装饰器，用于检查用户是否有管理员权限
function checkRoles(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor,
) {
  const originalMethod = descriptor.value;
  // 修改属性描述符的 value 属性，用新的函数替换原始方法
  descriptor.value = function (...args: any[]) {
    let user = users[args[0]];
    if (user && user.roles.includes('admin')) {
      return originalMethod.apply(this, args);
    } else {
      console.log('权限不足');
    }
  };
  return descriptor;
}

class AdminPanel {
  @checkRoles
  deleteUser(id: string) {
    console.log(`删除用户 ${id}`);
  }
}

const adminPanel = new AdminPanel();
adminPanel.deleteUser('001'); // 删除用户 001
adminPanel.deleteUser('002'); // 权限不足
export {};

// 2.2.3.3 权限检查
// 可以在方法调用前检查用户权限，决定是否允许调用。

function authorize(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor,
) {
  const originalMethod = descriptor.value;
  descriptor.value = function (...args: any[]) {
    const user = { roles: ['admin'] };
    if (!user.roles.includes('admin')) {
      throw new Error('User is not authorized to call this method');
    }
    return originalMethod.apply(this, args);
  };
  return descriptor;
}
class AdminPane5 {
  @authorize
  deleteUser(userId: string) {
    console.log(`User ${userId} deleted`);
  }
}
const adminPanel4 = new AdminPane5();
adminPanel4.deleteUser('123'); // User 123 deleted

//  方法缓存  可以缓存方法的返回结果，以提高性能

/**
 * 缓存装饰器，用于缓存方法的调用结果
 * 主要目的是避免对相同参数的重复调用，从而优化性能
 * @param target 目标类的原型
 * @param propertyKey 方法的名称
 * @param descriptor 属性描述符，用于存储方法的元数据
 */
function cache(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor,
) {
  // 保存原始方法，以便后续调用
  const originalMethod = descriptor.value;
  // 创建一个Map对象，用于缓存调用参数和结果
  const cacheMap = new Map<string, any>();

  // 重写方法的描述符，以实现缓存逻辑
  descriptor.value = function (...args: any[]) {
    // 将参数序列化为字符串，作为缓存的键
    const key = JSON.stringify(args);
    // 如果缓存中已存在该调用结果，则直接返回
    if (cacheMap.has(key)) {
      return cacheMap.get(key);
    }
    // 如果缓存中不存在，则调用原始方法，并将结果缓存
    const result = originalMethod.apply(this, args);
    cacheMap.set(key, result);
    return result;
  };
  // 返回修改后的属性描述符
  return descriptor;
}
class MathOperations {
  @cache
  factorial(n: number): number {
    if (n <= 1) {
      return 1;
    }
    return n * this.factorial(n - 1);
  }
}
const mathOps = new MathOperations();
console.log(mathOps.factorial(5)); // 120
console.log(mathOps.factorial(5)); // 从缓存中获取结
